# 舒尔特方格训练程序
import pygame
import sys
import time
import random
import json
from random import randint as ri
from datetime import datetime

# 颜色定义
GRAY = (163, 154, 123)  # 灰色 - 默认按钮颜色
BLUE = (189, 202, 218)  # 蓝色 - 背景色
CYAN = (144, 243, 215)  # 青色 - 高亮文字/按钮
WHITE = (255, 255, 255)  # 白色 - 普通文字
WRONG = (225, 143, 48)  # 橙色 - 错误提示
GREEN = (34, 135, 0)  # 绿色 - 已完成方格
RED = (255, 0, 0)  # 红色 - 最佳记录标记

# 游戏常量
FPS = 30  # 游戏帧率
GRID_SIZES = [3, 4, 5, 6]  # 可选的网格尺寸
DEFAULT_SIZE = 3  # 默认网格尺寸
GAME_MODES = ["标准模式", "找不同", "速算"]  # 游戏模式选项

# 初始化pygame
pygame.init()
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption("舒尔特方格训练", "yc")

# 字体设置（尝试加载中文字体，失败则使用系统字体）
try:
    # 主字体
    font = pygame.font.Font("yc_extrafile/HYWenHei_85W.ttf", 30)
    # 小号字体 - 用于辅助信息
    small_font = pygame.font.Font("yc_extrafile/HYWenHei_85W.ttf", 20)
    # 中号字体 - 用于次级标题
    medium_font = pygame.font.Font("yc_extrafile/HYWenHei_85W.ttf", 25)
    # 大号字体 - 用于主标题
    large_font = pygame.font.Font("yc_extrafile/HYWenHei_85W.ttf", 40)
    # 数字字体 - 专门用于显示方格数字
    num_font = pygame.font.Font("yc_extrafile/HYWenHei_85W.ttf", 45)
except:
    # 如果找不到自定义字体，使用系统默认中文字体
    font = pygame.font.SysFont('SimHei', 30)
    small_font = pygame.font.SysFont('SimHei', 20)
    medium_font = pygame.font.SysFont('SimHei', 25)
    large_font = pygame.font.SysFont('SimHei', 40)
    num_font = pygame.font.SysFont('SimHei', 45)


class GameState:
    """游戏状态管理类，负责保存游戏的各种状态和数据"""

    def __init__(self):
        self.current_screen = "main"  # 当前界面：main(主菜单)/game(游戏)/records(记录)
        self.grid_size = DEFAULT_SIZE  # 当前选择的网格尺寸
        self.game_mode = "标准模式"  # 当前游戏模式
        self.game_started = False  # 游戏是否已开始
        self.game_over = False  # 游戏是否结束
        self.start_time = 0  # 游戏开始时间戳
        self.end_time = 0  # 游戏结束时间戳
        self.current_number = 1  # 当前需要点击的数字
        self.records = []  # 游戏记录列表
        self.load_records()  # 加载历史记录
        self.show_next_hint = True  # 新增：是否显示下一个提示
        self.differences = []  # 找不同模式的不同位置
        self.math_problem = ""  # 速算模式的题目
        self.poem_lines = []  # 古诗模式的诗句
        self.answer = None  # 新增：存储速算模式的正确答案
        self.next_hint = None
        self.error_cells = []  # 格式：[{"index": 单元格索引, "time": 错误发生时间}]

    def load_records(self):
        """从JSON文件加载历史记录"""
        try:
            with open('records.json', 'r', encoding='utf-8') as f:
                self.records = json.load(f)
        except (FileNotFoundError, json.JSONDecodeError):
            # 如果文件不存在或格式错误，初始化为空列表
            self.records = []

    def save_records(self):
        """保存记录到JSON文件"""
        with open('records.json', 'w', encoding='utf-8') as f:
            json.dump(self.records, f, ensure_ascii=False)

    def add_record(self, time_taken, size, mode):
        """添加新的游戏记录"""
        record = {
            "date": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
            "用时": time_taken,  # 改为中文键名
            "尺寸": size,  # 改为中文键名
            "模式": mode  # 改为中文键名
        }
        self.records.append(record)
        self.save_records()

    def get_best_record(self, size=None, mode=None):
        """获取最佳记录，可以按尺寸和模式筛选"""
        if not self.records:
            return None

        filtered = self.records
        if size:
            # 改为中文键名
            filtered = [r for r in filtered if r['尺寸'] == size]
        if mode:
            # 改为中文键名
            filtered = [r for r in filtered if r['模式'] == mode]

        if not filtered:
            return None

        return min(filtered, key=lambda x: x['用时'])  # 改为中文键名


def generate_grid(state):
    if state.game_mode == "标准模式":
        numbers = list(range(1, state.grid_size**2 +1))
        random.shuffle(numbers)
        state.next_hint = 1  # 初始提示为第一个数字
        return numbers

    elif state.game_mode == "找不同":
        # 生成两个几乎相同的网格，随机3处不同
        base = [ri(1,50) for _ in range(state.grid_size**2)]
        diff = base.copy()
        for _ in range(3):
            idx = ri(0, len(diff)-1)
            diff[idx] += 10  # 明显差异
        state.differences = [i for i, (b,d) in enumerate(zip(base, diff)) if b != d]
        state.next_hint = state.differences.copy()  # 存储所有差异位置
        return diff

    elif state.game_mode == "速算":
        # 生成数学题目
        a, b = ri(1, 20), ri(1, 20)
        op = random.choice(["+", "-", "×", "÷"])
        if op == "+" : answer = a + b
        elif op == "-" : answer = a - b
        elif op == "×" : answer = a * b
        else: answer = a // b
        state.math_problem = f"{a} {op} {b} = ?"
        state.answer = answer  # 将答案存入state对象
        state.next_hint = answer  # 存储正确答案
        # 将答案放在随机位置
        cells = [ri(1, 50) for _ in range(state.grid_size**2 -1)]
        cells.insert(ri(0, len(cells)), answer)
        return cells

    elif state.game_mode == "古诗填空":
        # 示例古诗库
        poems = [
            "白日依山尽，黄河入海流。欲穷千里目，更上一层楼。",
            "床前明月光，疑是地上霜。举头望明月，低头思故乡。"
        ]
        selected = random.choice(poems).split("，")
        random.shuffle(selected)
        state.poem_lines = selected
        return selected


def draw_button(screen, x, y, width, height, text, font, bg_color, text_color, border=0):
    """
    绘制按钮的通用函数
    参数：
    - screen: 绘制目标表面
    - x,y: 按钮左上角坐标
    - width,height: 按钮尺寸
    - text: 按钮文字
    - font: 字体对象
    - bg_color: 背景色
    - text_color: 文字颜色
    - border: 边框宽度(0表示填充)
    """
    pygame.draw.rect(screen, bg_color, (x, y, width, height), border)
    text_surf = font.render(text, True, text_color)
    text_rect = text_surf.get_rect(center=(x + width / 2, y + height / 2))
    screen.blit(text_surf, text_rect)
    return pygame.Rect(x, y, width, height)  # 返回按钮的矩形区域用于碰撞检测


def draw_main_menu(state):
    """绘制主菜单界面"""
    screen.fill(BLUE)  # 蓝色背景

    # 绘制标题
    title = large_font.render("舒尔特方格训练", True, WHITE)
    screen.blit(title, (400 - title.get_width() / 2, 50))

    # 绘制网格尺寸选择区域
    size_text = medium_font.render("选择网格尺寸:", True, WHITE)
    screen.blit(size_text, (150, 150))

    size_buttons = []
    for i, size in enumerate(GRID_SIZES):
        # 绘制尺寸选择按钮，当前选中的按钮颜色不同
        btn = draw_button(screen, 150 + i * 120, 200, 100, 60, f"{size}x{size}",
                          font, GRAY if size != state.grid_size else CYAN,
                          CYAN if size != state.grid_size else GRAY)
        size_buttons.append((btn, size))

    # 绘制游戏模式选择区域
    mode_text = medium_font.render("选择游戏模式:", True, WHITE)
    screen.blit(mode_text, (150, 300))

    mode_buttons = []
    for i, mode in enumerate(GAME_MODES):
        # 绘制模式选择按钮，当前选中的按钮颜色不同
        btn = draw_button(screen, 150 + i * 150, 350, 140, 60, mode,
                          small_font, GRAY if mode != state.game_mode else CYAN,
                          CYAN if mode != state.game_mode else GRAY)
        mode_buttons.append((btn, mode))

    # 绘制功能按钮
    start_btn = draw_button(screen, 200, 450, 150, 70, "开始游戏", font, GRAY, CYAN)
    records_btn = draw_button(screen, 450, 450, 150, 70, "查看记录", font, GRAY, CYAN)

    # 新增设置区域
    setting_text = medium_font.render("游戏设置:", True, WHITE)
    screen.blit(setting_text, (550, 120))

    # 显示提示开关
    hint_btn = draw_button(screen, 550, 150, 200, 40,
                           "显示下一个提示: " + ("开" if state.show_next_hint else "关"),
                           small_font, GRAY, CYAN)

    return size_buttons, mode_buttons, start_btn, records_btn, hint_btn


def draw_game_screen(state, grid_numbers):
    """绘制游戏界面"""
    screen.fill(BLUE)  # 蓝色背景

    # 计算网格尺寸和位置
    cell_size = 80 if state.grid_size <= 5 else 60  # 根据网格大小调整单元格尺寸
    grid_width = state.grid_size * cell_size
    grid_height = state.grid_size * cell_size
    start_x = (800 - grid_width) // 2  # 水平居中
    start_y = (600 - grid_height) // 2 - 50  # 垂直居中偏上

    # 绘制网格和数字
    cells = []
    for i in range(state.grid_size):
        for j in range(state.grid_size):
            idx = i * state.grid_size + j
            num = grid_numbers[idx]
            x = start_x + j * cell_size
            y = start_y + i * cell_size
            num = grid_numbers[i * state.grid_size + j]

            # 根据数字状态决定单元格颜色
            if num < state.current_number:
                color = GREEN  # 已找到的数字 - 绿色
            elif num == state.current_number:
                # 根据提示状态决定颜色
                if state.show_next_hint and num == state.current_number:
                    color = CYAN  # 开启提示时显示青色
                else:
                    color = GRAY  # 关闭提示时显示灰色
            else:
                color = GRAY  # 未到达的数字 - 灰色

            pygame.draw.rect(screen, color, (x, y, cell_size - 5, cell_size - 5))

            # --- 找不同模式提示逻辑 ---
            if state.game_started and not state.game_over:  # 新增条件
                if state.show_next_hint and state.game_mode == "找不同":
                    if idx in state.differences:
                        # 绘制金色边框（仅在提示开启时显示）
                        pygame.draw.rect(screen, (255, 215, 0),  # 金色
                                         (x - 2, y - 2, cell_size - 1, cell_size - 1), 4)

            # 绘制数字（当前目标数字用白色,提示关闭时当前数字文字为青色,其他用青色）
            if num == state.current_number:
                text_color = WHITE if state.show_next_hint else CYAN  # 根据提示状态切换颜色
            else:
                text_color = CYAN
            num_text = num_font.render(str(num), True, text_color)
            num_rect = num_text.get_rect(center=(x + cell_size / 2 - 2, y + cell_size / 2 - 2))
            screen.blit(num_text, num_rect)

            # 保存单元格信息用于点击检测
            cells.append((pygame.Rect(x, y, cell_size - 5, cell_size - 5), num))

    # 绘制游戏信息（用时和当前目标）
    if state.game_started and not state.game_over:
        elapsed = time.time() - state.start_time
        time_text = font.render(f"用时: {elapsed:.2f}秒", True, WHITE)
        screen.blit(time_text, (20, 20))

        target_text = font.render(f"目标: {state.current_number}", True, WHITE)
        screen.blit(target_text, (20, 60))

        # 模式特定显示
        if state.game_mode == "速算":
            problem_text = medium_font.render(state.math_problem, True, WHITE)
            screen.blit(problem_text, (400 - problem_text.get_width() / 2, 100))

        elif state.game_mode == "古诗填空":
            for i, line in enumerate(state.poem_lines):
                text = medium_font.render(line, True, CYAN)
                screen.blit(text, (50, 100 + i * 40))

        # 找不同模式特殊处理
        if state.game_mode == "找不同" and state.game_started:
            for idx in state.differences:
                x = idx % state.grid_size
                y = idx // state.grid_size
                num = grid_numbers[idx]
                # 在原有数字上覆盖绘制红色文字
                red_text = num_font.render(str(num), True, RED)
                screen.blit(red_text, (start_x + x * cell_size + cell_size // 2 - red_text.get_width() // 2,
                                       start_y + y * cell_size + cell_size // 2 - red_text.get_height() // 2))

    # 绘制功能按钮
    restart_btn = draw_button(screen, 650, 20, 120, 50, "重新开始", small_font, GRAY, CYAN)
    menu_btn = draw_button(screen, 650, 80, 120, 50, "返回菜单", small_font, GRAY, CYAN)

    # 绘制错误闪烁效果
    current_time = time.time()
    for error in state.error_cells[:]:  # 遍历副本以便删除
        idx = error["index"]
        elapsed = current_time - error["time"]

        # 闪烁逻辑：0.2秒显示->0.2秒隐藏->0.2秒显示->0.2秒隐藏
        if elapsed < 0.8:  # 总持续时间0.8秒
            i = idx // state.grid_size
            j = idx % state.grid_size
            x = start_x + j * cell_size
            y = start_y + i * cell_size

            # 根据时间判断是否显示红色（0-0.2和0.4-0.6秒显示）
            if (elapsed % 0.4) < 0.2:
                pygame.draw.rect(screen, RED, (x, y, cell_size - 5, cell_size - 5))
        else:
            state.error_cells.remove(error)  # 移除过期错误

    # 显示提示逻辑（添加在游戏信息区域）
    if state.show_next_hint and not state.game_over:
        if state.game_mode == "标准模式":
            # 高亮下一个目标数字
            for idx, (rect, num) in enumerate(cells):
                if num == state.current_number:
                    pygame.draw.rect(screen, RED, rect, 3)

        elif state.game_mode == "找不同":
            # 标记剩余差异点
            for idx in state.differences:
                x = idx % state.grid_size
                y = idx // state.grid_size
                # pygame.draw.circle(screen, RED,
                #                    (start_x + x * cell_size + cell_size // 2,
                #                     start_y + y * cell_size + cell_size // 2), 8)

        elif state.game_mode == "速算":
            # 在题目旁显示答案
            answer_text = medium_font.render(f"答案: {state.answer}", True, RED)
            screen.blit(answer_text, (400 - answer_text.get_width() // 2, 150))

    # 游戏结束时的显示
    if state.game_over:
        elapsed = state.end_time - state.start_time  # 计算总用时
        # best = state.get_best_record(state.grid_size, state.game_mode)  # 获取最佳记录
        best = state.get_best_record(state.grid_size, state.game_mode)  # 参数自动匹配中文键名

        # 游戏完成提示
        msg = large_font.render("游戏完成!", True, WHITE)
        screen.blit(msg, (400 - msg.get_width() / 2, 450))

        # 显示本次用时
        time_msg = font.render(f"您的用时: {elapsed:.2f}秒", True, WHITE)
        screen.blit(time_msg, (400 - time_msg.get_width() / 2, 500))

        # 如果存在最佳记录，显示最佳成绩
        if best:
            # best_msg = small_font.render(f"最佳记录: {best['time']:.2f}秒", True, WHITE)
            best_msg = small_font.render(f"最佳记录: {best['用时']:.2f}秒", True, WHITE)
            screen.blit(best_msg, (400 - best_msg.get_width() / 2, 540))

        # 新增错误点击记录
        error_cells = []

        # 绘制错误方块
        for cell, num in error_cells:
            pygame.draw.rect(screen, RED, cell)
            num_text = num_font.render(str(num), True, WHITE)
            screen.blit(num_text, cell.center)

        # 每帧清除旧错误（或使用计时器）
        del error_cells[:]

    return cells, restart_btn, menu_btn


def draw_records_screen(state):
    """绘制记录查看界面"""
    screen.fill(BLUE)  # 蓝色背景

    # 标题
    title = large_font.render("游戏记录", True, WHITE)
    screen.blit(title, (400 - title.get_width() / 2, 50))

    # 表头
    headers = ["日期", "模式", "尺寸", "用时(秒)", "最佳"]
    for i, header in enumerate(headers):
        text = small_font.render(header, True, CYAN)
        screen.blit(text, (100 + i * 150, 120))

    # 显示记录内容
    if not state.records:
        # 无记录时的提示
        no_records = font.render("暂无记录，快去玩游戏吧!", True, WHITE)
        screen.blit(no_records, (400 - no_records.get_width() / 2, 200))
    else:
        # 按日期降序排序（最新的在前）
        sorted_records = sorted(state.records, key=lambda x: x['date'], reverse=True)

        # 显示最近10条记录
        for i, record in enumerate(sorted_records[:10]):
            # 检查是否是当前尺寸和模式的最佳记录
            is_best = state.get_best_record(record['尺寸'], record['模式'])['用时'] == record['用时']

            # 准备要显示的记录数据
            data = [

                record['date'][5:16],  # 缩短日期显示（去掉年份）
                record['模式'],  # 游戏模式
                f"{record['尺寸']}x{record['尺寸']}",  # 网格尺寸
                f"{record['用时']:.2f}",  # 用时（保留2位小数）
                "★" if is_best else ""  # 最佳记录标记
            ]

            # 绘制每列数据
            for j, item in enumerate(data):
                color = RED if is_best and j == len(data) - 1 else WHITE  # 最佳记录用红色标记
                text = small_font.render(item, True, color)
                screen.blit(text, (100 + j * 150, 150 + i * 30))

    # 返回按钮
    back_btn = draw_button(screen, 400 - 75, 500, 150, 60, "返回", font, GRAY, CYAN)

    return back_btn


def main():
    """主游戏循环"""
    clock = pygame.time.Clock()  # 游戏时钟
    state = GameState()  # 游戏状态实例
    grid_numbers = []  # 网格数字存储
    running = True  # 游戏运行标志

    while running:
        # 根据当前界面调用不同的绘制函数
        if state.current_screen == "main":
            size_buttons, mode_buttons, start_btn, records_btn, hint_btn = draw_main_menu(state)
        elif state.current_screen == "game":
            cells, restart_btn, menu_btn = draw_game_screen(state, grid_numbers)
        elif state.current_screen == "records":
            back_btn = draw_records_screen(state)

        pygame.display.flip()  # 更新显示

        # 事件处理循环
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False  # 退出游戏
            # 鼠标点击事件处理
            if event.type == pygame.MOUSEBUTTONDOWN:
                pos = pygame.mouse.get_pos()  # 获取鼠标位置
                # 游戏界面点击处理
                if state.current_screen == "game" and state.game_started and not state.game_over:
                    for cell, num in cells:  # 使用主循环中的cells变量
                        if cell.collidepoint(pos):
                            # 标准模式错误判断
                            if state.game_mode == "标准模式":
                                if num == state.current_number:
                                    state.current_number += 1
                                    state.next_hint = state.current_number  # 更新提示为下一个数字
                                    # 检查是否完成
                                    if state.current_number > state.grid_size ** 2:
                                        state.game_over = True
                                        state.end_time = time.time()
                                        elapsed = state.end_time - state.start_time
                                        state.add_record(elapsed, state.grid_size, state.game_mode)
                                else:
                                    idx = cells.index((cell, num))
                                    state.error_cells.append({"index": idx, "time": time.time()})

                            elif state.game_mode == "找不同":
                                idx = cells.index((cell, num))
                                if idx in state.differences:
                                    state.differences.remove(idx)
                                    state.next_hint = state.differences.copy()  # 更新剩余差异
                                    if not state.differences:
                                        state.game_over = True
                                        state.end_time = time.time()
                                        elapsed = state.end_time - state.start_time
                                        state.add_record(elapsed, state.grid_size, state.game_mode)
                                # 找不同模式错误判断
                                else:
                                    state.error_cells.append({"index": idx, "time": time.time()})

                            elif state.game_mode == "速算":
                                if num == state.answer:  # 需确保answer已定义（根据generate_grid逻辑）
                                    state.game_over = True
                                    state.end_time = time.time()
                                    elapsed = state.end_time - state.start_time
                                    state.add_record(elapsed, state.grid_size, state.game_mode)
                                # 速算模式错误判断
                                else:
                                    idx = cells.index((cell, num))
                                    state.error_cells.append({"index": idx, "time": time.time()})

                            elif state.game_mode == "古诗填空":
                                correct_order = sorted(state.poem_lines)  # 示例逻辑，需根据实际古诗模式调整
                                if num == correct_order[state.current_number]:
                                    state.current_number += 1
                                    if state.current_number >= len(correct_order):
                                        state.game_over = True
                                        state.end_time = time.time()
                                        elapsed = state.end_time - state.start_time
                                        state.add_record(elapsed, state.grid_size, state.game_mode)

                if hint_btn.collidepoint(pos):
                    state.show_next_hint = not state.show_next_hint
                # 主菜单界面事件处理
                if state.current_screen == "main":
                    # 处理网格尺寸选择
                    for btn, size in size_buttons:
                        if btn.collidepoint(pos):
                            state.grid_size = size  # 更新选中尺寸

                    # 处理游戏模式选择
                    for btn, mode in mode_buttons:
                        if btn.collidepoint(pos):
                            state.game_mode = mode  # 更新选中模式

                    # 开始游戏按钮
                    if start_btn.collidepoint(pos):
                        state.current_screen = "game"
                        state.game_started = False
                        state.game_over = False
                        state.current_number = 1
                        grid_numbers = generate_grid(state)  # 生成新网格

                    # 查看记录按钮
                    if records_btn.collidepoint(pos):
                        state.current_screen = "records"

                # 游戏界面事件处理
                elif state.current_screen == "game":
                    # 游戏未开始时点击开始计时
                    if not state.game_started and not state.game_over:
                        state.game_started = True
                        state.start_time = time.time()

                    # 处理网格点击
                    for cell, num in cells:
                        if cell.collidepoint(pos) and state.game_started and not state.game_over:
                            if num == state.current_number:  # 点击了正确的数字
                                state.current_number += 1
                                # 检查是否完成所有数字
                                if state.current_number > state.grid_size * state.grid_size:
                                    state.game_over = True
                                    state.end_time = time.time()
                                    elapsed = state.end_time - state.start_time
                                    state.add_record(elapsed, state.grid_size, state.game_mode)

                    # 重新开始按钮
                    if restart_btn.collidepoint(pos):
                        state.game_started = False
                        state.game_over = False
                        state.current_number = 1
                        grid_numbers = generate_grid(state)

                    # 返回菜单按钮
                    if menu_btn.collidepoint(pos):
                        state.current_screen = "main"

                # 记录界面事件处理
                elif state.current_screen == "records":
                    # 返回按钮
                    if back_btn.collidepoint(pos):
                        state.current_screen = "main"

        clock.tick(FPS)  # 控制游戏帧率

    pygame.quit()  # 退出pygame
    sys.exit()  # 退出程序


if __name__ == "__main__":
    main()